package org.epidroid.epidroid.androsoul.ui.conversation;

import java.util.Timer;
import java.util.TimerTask;

import org.epidroid.epidroid.R;
import org.epidroid.epidroid.androsoul.engine.AndroSoul;
import org.epidroid.epidroid.androsoul.engine.communication.Connection;
import org.epidroid.epidroid.androsoul.engine.contacts.NetsoulContact;
import org.epidroid.epidroid.androsoul.engine.conversation.Conversation;
import org.epidroid.epidroid.androsoul.engine.conversation.MessageItem;
import org.epidroid.epidroid.androsoul.engine.service.AndroSoulService;
import org.epidroid.epidroid.androsoul.engine.service.AndroSoulServiceBinder;
import org.epidroid.epidroid.androsoul.engine.service.IAndroSoulService;
import org.epidroid.epidroid.androsoul.engine.service.IAndroSoulServiceListener;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Message;
import android.util.Log;
import android.view.GestureDetector;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnKeyListener;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

/**
 * @author conrath 
 * 	Handle a conversation TODO: when a message is received, the
 *         list of messages is not instantaneously updated… need to call
 *         notifyDataSetChanged on the adapter from the Androsoul activity.
 */
public class ConversationActivity extends Activity {

	// Constants
	public static final int UPDATE = 0;
	public static final int TYPING_DELAY = 1;

	// AndroSoul service
	private IAndroSoulService _service = null;

	// The contact we are talking to
	private String _name = null;
	private NetsoulContact _contact = null;
	private Conversation _conversation = null;
	// ListView that contains the messages
	private ListView _messageList = null;
	// Contact dictionary
	//private ContactDictionary _dictionary = null;
	// Adapter for the list of messages
	private ArrayAdapter<MessageItem> _arrayAdapter = null;
	// TextView used to write "contact is typing�"
	private TextView _typingTexView = null;
	private GestureDetector _gestureDetector = null;
	private View.OnTouchListener _gestureListener = null;

	private Timer _timer = null;
	private TimerTask _sendStopWrittingTask = null;
	private Boolean _timerFirstTime = true;

	private ServiceConnection _serviceConnection = null;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		setContentView(R.layout.androsoul_conversation);
		_messageList = (ListView) findViewById(R.id.messageList);
		Bundle extras = getIntent().getExtras();
		String name = (extras != null ? extras.getString("contact")
				: (savedInstanceState != null ? savedInstanceState
						.getString("contact") : null));
		_name = name;
		_typingTexView = (TextView) findViewById(R.id.isTyping);
		bindService();
		initGestureDetector();

		initTimer();
	}

	@Override
	protected void onDestroy() {

		unbindService(_serviceConnection);

		super.onDestroy();
	}

	private void initTimer() {
		_timer = new Timer();
		_sendStopWrittingTask = new TimerTask() {

			@Override
			public void run() {
				if (_service.getConnectionState() == Connection.State.AUTHENTIFIED) {
					_service.sendContactStopTyping(_contact);
				}
			}
		};
	}

	/*
	 * Called when the user presses a key handle the sending of the user is
	 * typing message
	 */
	private void keyDown() {
		try {
			long time = _sendStopWrittingTask.scheduledExecutionTime();
			Log.e(AndroSoul.LOGCAT, "START " + time);
			if ((time > 0) || (_timerFirstTime)) {
				if (_service.getConnectionState() == Connection.State.AUTHENTIFIED) {
					_service.sendContactIsTyping(_contact);
				}
				_timerFirstTime = false;
			}

			_timer.purge();
			_timer.cancel();
			initTimer();
			_timer.schedule(_sendStopWrittingTask, 1000 * TYPING_DELAY);
		} catch (Exception e) {
			Log.e(AndroSoul.LOGCAT, e.getMessage());
		}
	}

	/**
	 * Initialize the gesture detector Detector left and right flings
	 */
	private void initGestureDetector() {
		_gestureDetector = new GestureDetector(new ConversationGestureDetector(
				new Runnable() {

					@Override
					public void run() {
						switchLeft();
					}

				}, new Runnable() {

					@Override
					public void run() {
						switchRight();
					}

				}));
		_gestureListener = new View.OnTouchListener() {
			public boolean onTouch(View v, MotionEvent event) {
				if (_gestureDetector.onTouchEvent(event)) {
					return true;
				}
				return false;
			}
		};

		_messageList.setOnTouchListener(_gestureListener);
	}

	/**
	 * Bind the activity to the androsoul service
	 */
	private void bindService() {
		Intent intent = new Intent(this, AndroSoulService.class);

		final IAndroSoulServiceListener listener = new IAndroSoulServiceListener() {
			public void dataChanged(final Message data) {
				ConversationActivity.this.runOnUiThread(new Runnable() {
					public void run() {
						ReceiveAction(data);
					}
				});
			}
		};

		_serviceConnection = new ServiceConnection() {
			public void onServiceConnected(ComponentName name,
					IBinder servicebinder) {
				IAndroSoulService service = ((AndroSoulServiceBinder) servicebinder)
						.getService();
				service.addListener(listener);
				_service = service;
				_service.enableNotifs(false);
				NetsoulContact c = _service.getContactFromName(_name);
				if (c == null)
					return;
				switchToContact(c);
			}

			public void onServiceDisconnected(ComponentName name) {
			}
		};

		bindService(intent, _serviceConnection, Context.BIND_AUTO_CREATE);
	}

	/**
	 * Initialize data and user interface corresponding to the new contact we
	 * are talking to
	 * 
	 * @param c
	 *            Contact to whom we are now talking
	 */
	private void switchToContact(NetsoulContact c) {
		_contact = c;
		_name = c.getName();
		_conversation = _contact.getConversation();
		_arrayAdapter = new ConversationMessageAdapter(_service.getMySelf(),
				this, R.layout.androsoul_message_item,
				_conversation.getMessages(), _service);
		_messageList.setAdapter(_arrayAdapter);

		ImageView img = (ImageView) findViewById(R.id.imgcontact);
		Drawable tmp = _contact.getImage(this, false);
		if (tmp != null)
			img.setImageDrawable(tmp);
		else
			img.setImageResource(R.drawable.unknownbuddy);

		TextView title = (TextView) findViewById(R.id.conversationTitle);
		title.setText(_name);

		createEditTextHandler();
		_conversation.clearNbUnread();
	}

	@Override
	public void onStart() {
		super.onStart();
		// Load history
		// TODO
		/*
		 * try { mConversation.loadHistory(openFileInput("conversation_" +
		 * mContact.getName())); } catch (FileNotFoundException e) { // No file
		 * }
		 */
	}

	@Override
	public void onPause() {
		super.onPause();
		_conversation.clearNbUnread();
		if (_service != null)
			_service.enableNotifs(true);
		// Save history
		/*
		 * try { mConversation .saveHistory(openFileOutput( "conversation_" +
		 * mContact.getName(), Context.MODE_PRIVATE)); } catch
		 * (FileNotFoundException e) { // TODO }
		 */
		// mMessageQueue.flush(mContact.getName());
	}

	@Override
	protected void onResume() {
		super.onResume();
		if (_service != null)
			_service.enableNotifs(false);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see android.app.Activity#onNewIntent(android.content.Intent) TODO:
	 * figure out why it is not called!!
	 */
	@Override
	protected void onNewIntent(Intent intent) {
		Log.v(AndroSoul.LOGCAT, "onNewIntent");
		super.onNewIntent(intent);

		// setIntent(intent);
		String name = intent.getExtras().getString("contact");
		Log.v(AndroSoul.LOGCAT, "switching to " + name);
		NetsoulContact c = _service.getContactFromName(name);

		switchToContact(c);
	}

	/*
	 * Switch to the previous conversation
	 */
	private void switchLeft() {
		Conversation prev = _service.getOpenedConversations().getPrev(
				_conversation);
		if (prev == null)
			return;
		switchToContact(prev.getContact());
	}

	/*
	 * Switch to the next conversation
	 */
	private void switchRight() {
		Conversation next = _service.getOpenedConversations().getNext(
				_conversation);
		if (next == null)
			return;
		switchToContact(next.getContact());
	}

	/**
	 * Called when the androsoul service notifies an action
	 * 
	 * @param msg
	 *            Message containing the action's properties
	 */
	public void ReceiveAction(Message msg) {
		switch (msg.what) {
		case AndroSoul.RECEIVE_MESSAGE:
			MessageItem item = (MessageItem) msg.obj;
			if (item.getAuthor().equals(_contact.getName()))
				_arrayAdapter.notifyDataSetChanged();
			break;
		case AndroSoul.USER_TYPING:
			if (((NetsoulContact) msg.obj).getName().equals(_name))
				userTyping();
			break;
		case AndroSoul.USER_CANCEL_TYPING:
			if (((NetsoulContact) msg.obj).getName().equals(_name))
				userCancelTyping();
			break;
		}
	}

	/**
	 * Called when receiving a notification that the user is typing.
	 */
	public void userTyping() {
		_typingTexView.setText(_contact.getName() + " is writting...");
	}

	/**
	 * Called when receiving a notification that the user has stopped typing.
	 */
	public void userCancelTyping() {
		_typingTexView.setText("");
	}

	/**
	 * Add event on the text input When the user presses the enter key, send the
	 * message
	 */
	public void createEditTextHandler() {
		final EditText edittext = (EditText) findViewById(R.id.edittext);
		edittext.setOnKeyListener(new OnKeyListener() {
			public boolean onKey(View v, int keyCode, KeyEvent event) {

				// If the user presses the enter button
				if ((event.getAction() == KeyEvent.ACTION_DOWN)
						&& (keyCode == KeyEvent.KEYCODE_ENTER)) {
					// Only send message if we are connected, and if message not
					// empty
					if (edittext.getText().toString().equals(""))
						return true;
					if (_service.getConnectionState() != Connection.State.AUTHENTIFIED) {
						showToast("Cannot send message because you are offline!");
						return true;
					}
					_service.sendContactMessage(_contact, edittext.getText()
							.toString());
					_arrayAdapter.notifyDataSetChanged();
					edittext.setText("");

					return true;
				}

				if ((event.getAction() == KeyEvent.ACTION_DOWN)
						&& (keyCode != KeyEvent.KEYCODE_ENTER)) {
					keyDown();
				}

				return false;
			}
		});
	}

	/**
	 * Handy method for showing a toast
	 * 
	 * @param content
	 *            Content of the toast
	 */
	public void showToast(String content) {
		Toast toast = Toast.makeText(this, content, Toast.LENGTH_SHORT);
		toast.show();
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		MenuInflater inflater = getMenuInflater();
		inflater.inflate(R.menu.androsoul_conversation_menu, menu);
		return true;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see android.app.Activity#onOptionsItemSelected(android.view.MenuItem)
	 * Show the button that enables the user to send a wizz
	 */
	public boolean onOptionsItemSelected(MenuItem item) {
		switch (item.getItemId()) {
		case R.id.b_wizz:
			sendWizz();
			break;
		default:
			break;
		}
		return true;
	}

	private void sendWizz() {
		if (_service.getConnectionState() != Connection.State.AUTHENTIFIED) {
			showToast("Cannot send wizz because you are offline!");
			return;
		}
		_service.sendContactWizz(_contact);
	}
}
